﻿#region BSD 3-Clause License
/* Copyright (c) 2012, Intel Corporation
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *    
 * 3. Neither the name of Intel Corporation nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using SpartanUnit;
using OpenHopla.Timing;

namespace OpenHopla.UnitTests.Root.Timing
{
    class TimerShould : TestSuite
    {
        #region Test Fixture
        private Timer TestTimer = null;

        private ICollection<TimeSpan> TestTimes = null;

        private readonly TimeSpan OneTick = TimeSpan.FromTicks(1);
        private readonly TimeSpan OneMillisecond = TimeSpan.FromMilliseconds(1);
        private readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
        private readonly TimeSpan OneMinute = TimeSpan.FromMinutes(1);
        private readonly TimeSpan OneHour = TimeSpan.FromHours(1);
        private readonly TimeSpan OneDay = TimeSpan.FromDays(1);

        private void InitializeNewTimer()
        {
            TestTimer = new Timer();
        }

        private void InitializeTestTimes()
        {
            TestTimes = new Collection<TimeSpan>();
            TestTimes.Add(TimeSpan.MinValue);
            TestTimes.Add(TimeSpan.Zero);
            TestTimes.Add(OneTick);
            TestTimes.Add(OneMillisecond);
            TestTimes.Add(OneSecond);
            TestTimes.Add(OneMinute);
            TestTimes.Add(OneHour);
            TestTimes.Add(OneDay);
            TestTimes.Add(TimeSpan.MaxValue);
        }

        public override void Setup()
        {
            base.Setup();
            InitializeNewTimer();
            InitializeTestTimes();
        }
        #endregion

        #region Test Cases

        [TestCase]
        public void BeConstructible()
        {
            Assert.IsTrue(TestTimer is Timer);
        }

        [TestCase]
        public void HaveTimeRemainingGetterSetter()
        {
            foreach (TimeSpan value in TestTimes)
            {
                TestTimer.RemainingTime = value;
                string message = "Time remaining set to: " + value.ToString();

                if (value < TimeSpan.Zero)
                {
                    Assert.IsTrue(TestTimer.RemainingTime == TimeSpan.Zero, message);
                }
                else
                {
                    Assert.IsTrue(TestTimer.RemainingTime == value, message);
                }
            }
        }

        [TestCase]
        public void AlwaysHaveNonNegativeTimeRemaining()
        {
            foreach (TimeSpan value in TestTimes)
            {
                TestTimer.RemainingTime = value;
                Assert.IsTrue(TestTimer.RemainingTime >= TimeSpan.Zero);
            }
        }

        [TestCase]
        public void HaveZeroTimeRemainingIfNotOtherwiseSpecified()
        {
            Timer newTimer = new Timer();
            Assert.IsTrue(newTimer.RemainingTime == TimeSpan.Zero);
        }

        [TestCase]
        public void HaveConstructorThatSetsTimeRemaining()
        {
            foreach (TimeSpan value in TestTimes)
            {
                Timer newTimer = new Timer(value);
                string message = "Time remaining set to: " + value.ToString();

                if (value < TimeSpan.Zero)
                {
                    Assert.IsTrue(TestTimer.RemainingTime == TimeSpan.Zero, message);
                }
                else
                {
                    Assert.IsTrue(TestTimer.RemainingTime <= value, message);
                }
            }
        }

        [TestCase]
        public void HaveMethodThatStartsStopsTimer()
        {
            Assert.IsTrue(TestTimer.IsStopped);
            foreach (TimeSpan value in TestTimes)
            {
                TestTimer.RemainingTime = value;
                TestTimer.StartStop();
                Assert.IsTrue(TestTimer.IsRunning);
                TestTimer.StartStop();
                Assert.IsTrue(TestTimer.IsStopped);
            }
        }

        [TestCase]
        public void BeStoppedUntilStarted()
        {
            Assert.IsTrue(TestTimer.IsStopped);
            Assert.IsTrue(!TestTimer.IsRunning);
        }

        [TestCase]
        public void RunAfterBeingStarted()
        {
            TestTimer.StartStop();
            Assert.IsTrue(TestTimer.IsRunning);
            Assert.IsTrue(!TestTimer.IsStopped);
        }

        [TestCase]
        public void RunDownTimeRemainingToZero()
        {
            // On your mark.
            Assert.IsTrue(TestTimer.IsStopped);

            // Get set.
            TestTimer.RemainingTime = OneSecond;
            Assert.IsTrue(TestTimer.RemainingTime == OneSecond);

            // Go!
            TestTimer.StartStop();
            Assert.IsTrue(TestTimer.IsRunning);
            Assert.IsTrue(TestTimer.RemainingTime > TimeSpan.Zero);

            // Wait for the set period of time.
            DateTime startTime = DateTime.Now;
            TimeSpan elapsedTime = DateTime.Now - startTime;
            while (elapsedTime < OneSecond)
            {
                elapsedTime = DateTime.Now - startTime;
            }

            // Verify that timer has run down to zero.
            Assert.IsTrue(TestTimer.RemainingTime == TimeSpan.Zero);
        }

        #endregion
    }
}
